package com.liyk.shop.service.impl;

import com.liyk.shop.component.rabbitmq.OrderSender;
import com.liyk.shop.domain.entity.ProductKill;
import com.liyk.shop.domain.entity.ProductKillOrder;
import com.liyk.shop.dto.KillProductDetailDto;
import com.liyk.shop.dto.KillProductDto;
import com.liyk.shop.enums.KillEnum;
import com.liyk.shop.enums.OrderEnum;
import com.liyk.shop.enums.ProductEnum;
import com.liyk.shop.exception.GenericException;
import com.liyk.shop.utils.IdGenerateUtil;
import com.liyk.shop.utils.MD5Utils;
import com.liyk.shop.utils.RedissLockUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.liyk.shop.dao.ProductKillDao;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class ProductKillService extends BaseServiceImpl<ProductKill, ProductKillDao> {

    @Autowired
    private ProductKillDao productKillDao;

    @Autowired
    private OrderSender orderSender;

    @Autowired
    private ProductKillOrderService productKillOrderService;

    @Transactional
    public void executeKill(KillProductDto dto) {
        if (!MD5Utils.convertMd5Str(String.valueOf(dto.getKillId())).equals(dto.getSecret())) {
            throw new GenericException(ProductEnum.NO_KILL.getCode(), ProductEnum.NO_KILL.getMsg());
        }
        //查看库存
        ProductKill kill = this.findStock(dto.getKillId());
        if (Objects.isNull(kill)) {
            throw new GenericException(ProductEnum.UNDERSTOCK.getCode(), ProductEnum.UNDERSTOCK.getMsg());
        }
        String key = "" + dto.getUserId() + dto.getKillId();
        System.out.println(key);
        //redission 分布式锁-加锁   - jmeter压测的时候会导致锁重入。CurrentThreadId相等，导致重入
        boolean success = RedissLockUtil.tryLock(key, TimeUnit.MILLISECONDS,0, 300000);
        if (!success) {
            log.info("爆满，请稍后再试");
            return;
        }
        //是否限购 -- 高并发下会存在超卖问题，可以存入队列。以串行的形式操作。
        ProductKillOrder killOrder = productKillOrderService.getByUserIdAndKillId(dto.getUserId(), dto.getKillId());
        if (Objects.nonNull(killOrder)) {
            throw new GenericException(KillEnum.RESTRICTED_PURCHASE.getCode(), KillEnum.RESTRICTED_PURCHASE.getMsg());
        }
        try {
            //写入秒杀订单表
            ProductKillOrder order = new ProductKillOrder();
            order.setId(IdGenerateUtil.generate());
            order.setKillProductId(dto.getKillId());
            order.setUserId(dto.getUserId());
            order.setState(OrderEnum.NO.getCode());
            order.setUserEmail(dto.getEmail());
            order.setUserPhone(dto.getPhone());
            int i = this.decreaseStocks(dto.getKillId()); //减库存
            if (i > 0) {
                int i1 = productKillOrderService.insertSelective(order);
                //扣库存
                if (i1 > 0) {
                    //秒杀成功,rabbitmq异步发送通知
                    orderSender.sendOrderEmail(order);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            RedissLockUtil.unlock("" + dto.getUserId() + dto.getKillId());
        }

    }

    /**
     * @Description: 获取秒杀商品详情
     * @Param: [productId]
     * @return: com.liyk.shop.dto.KillProductDetailDto
     * @Author: liyk
     * @Date: 2019/12/12
     */
    public KillProductDetailDto getKillDetail(Integer killId) {
        KillProductDetailDto dto = new KillProductDetailDto();
        ProductKill bean = this.getById(killId);
        if (Objects.isNull(bean)) {
            throw new GenericException(ProductEnum.IS_NULL.getCode(), ProductEnum.IS_NULL.getMsg());
        }
        //秒杀商品信息
        Date endTime = bean.getEndTime();
        Date startTime = bean.getStartTime();
        Date currTime = new Date();
        BeanUtils.copyProperties(bean, dto);
        if (currTime.getTime() >= startTime.getTime() && currTime.getTime() <= endTime.getTime()) {
            //可以秒杀
            dto.setSecret(MD5Utils.convertMd5Str(String.valueOf(killId)));
            dto.setIsKill(true);
        } else {
            dto.setSecret("");
            dto.setIsKill(false);
        }
        return dto;
    }

    public ProductKill findStock(Integer id) {
        return productKillDao.findStock(id);
    }

    public int decreaseStocks(Integer id) {
        return productKillDao.decreaseStocks(id);
    }
}

